package org.itboys.mongodb.service;

import org.springframework.stereotype.Service;

/**
 * Created by maozg on 15/12/24.
 */
@Service
public class UniqueRandomNumberGenerator {
    private long factor = 0xffdfff;
    private long hfactor;
    private long salt = 0x298fe3;
    private long offset = 0x328ba6;
    private long upperLimit;

    public UniqueRandomNumberGenerator() {
        String factorPre = "ffdfff";
        String saltPre = "298fe3";
        String offsetPre = "328ba6";
        this.factor = Long.parseLong(factorPre, 16);
        if(this.factor <= 1L) {
            throw new RuntimeException("Invalid factor.");
        } else {
            this.hfactor = this.factor / 2L;
            this.salt = Long.parseLong(saltPre, 16);
            this.offset = Long.parseLong(offsetPre, 16);
            long f = this.factor;

            int count;
            for(count = 0; f > 0L; ++count) {
                f >>= 1;
            }

            this.upperLimit = (long)((1 << count) - 1);
            if(this.salt >= 1L && this.salt < this.upperLimit) {
                if(this.offset < 0L || this.offset >= this.upperLimit) {
                    throw new RuntimeException("Invalid offset.");
                }
            } else {
                throw new RuntimeException("Invalid salt.");
            }
        }
    }

    private long permute(long x) {
        if(x > this.factor) {
            return x;
        } else {
            long r = x * x % this.factor;
            if(x > this.hfactor) {
                r = this.factor - r;
            }

            return r;
        }
    }

    public long generate(long seqid) {
        long r = this.permute(seqid) + this.offset;
        if(r > this.upperLimit) {
            r -= this.upperLimit;
        }

        return this.permute(r ^ this.salt);
    }

    public long getUpperLimit() {
        return this.upperLimit;
    }
}
